#include "SessionTracker.h"

//===========================================================================
//
// Constructor: Parameters (1) sniffQ - pointer to the sniffer's raw data packets queue
//						   (2) sessTable - pointer to the sessions table
//
SessionTracker::SessionTracker(SharedQueue<struct RAWIPDATA>* sniffQ,map<string,struct HTTP_SESSION>* sessTable)
:_rawData(""),_snifferQueue(sniffQ),_httpSessionsTable(sessTable),_numIPPackets(0),_sessionTimeToLive(0),_maxSessions(0),_numPackets(0),_serverPort(0)
{
}

//===========================================================================
//
// Desctructor
//
SessionTracker::~SessionTracker()
{
}

//===========================================================================
//
// init() - reads and initilizes configuration for the session tracker from 'session_tracker.conf' file
//
void SessionTracker::init()
{
	string max;
	string ttl;
	const string conf_file="/opt/swaf/conf/session_tracker.conf";
	try
	{
		// Reading configuration from conf file
		ConfigFile cf(conf_file);
		max.assign(cf.Value("general","max_http_sessions"));
		_maxSessions=atoi(max.c_str());
		ttl.assign(cf.Value("general","session_ttl"));
		_sessionTimeToLive=atoi(ttl.c_str());
	}
	catch(...)
	{
	        cout <<"\nError --> Failed to read Session Tracker configuration."<<endl;
			cout <<"Check configuration file: "<<conf_file<<"\n"<<endl;
			exit(1);
	}

	//initilize the '_serverPort' member variable
	getServerPort();

}

//===========================================================================
//
// run() : this is the main method of the SessionTracker.
//		   endless loop runs:
//		   1. pop raw data packet from the sniffer queue
//		   2. parse the raw data
//		   3. update the sessions table with the relevant data
//
void SessionTracker::run()
{
	u_char *httpData;
	int httpDataLen;
	struct RAWIPDATA raw;
	struct TCP_PARAMS tcpParams;
	struct SEQACK_PARAMS seqackParams;
	struct HTTP_SESSION session;
	struct SESSION_STATE state={true,0,0,0};
	struct DATA httpdata;
	string sessionID;
	string currentData;

	while(1)
	{
		//get raw data packet from the sniffer queue
		_snifferQueue->pop(raw);

		// parse the raw data packet and return the length of the http data
		httpDataLen=parseRawData(raw,tcpParams,seqackParams,&httpData);

		if(httpDataLen==0) //this current raw data packet has no relevant http data
			continue;  // continue to pop and parse the next raw data packet

		_numPackets++;
		currentData.assign((char*)httpData,httpDataLen); //currentData string holds the packet's http data

		//get the session's ID from the tcp parameters in the parsed packet
		setSessionId(tcpParams,sessionID);

		if( _httpSessionsTable->count(sessionID)==0 ) // the first http data of the session
		{
			//assign the session's structs
			session.state=state;
			session.tcpParams=tcpParams;
			session.seqackParams=seqackParams;

			//insert the http session into the sessions table
			(*_httpSessionsTable)[sessionID]=session;
		}

		#ifdef DEBUG_SESSION_TRACKER
		cout<<"packet number: "<<_numPackets<<endl;
		cout<<"\n\n------------------------- START --------------------------------"<<endl;
		cout<<"Session Tracker enters data: \n"<<currentData<<endl;
		cout<<"--------------------------- END ------------------------------------------"<<endl;
		#endif

		(*_httpSessionsTable)[sessionID].seqackParams=seqackParams;

		// the session has new http data - so update the session's  new data state
		(*_httpSessionsTable)[sessionID].state.newData=true;


		if(tcpParams.dst_port==_serverPort) // request
		{
			httpdata.type=0;
			(*_httpSessionsTable)[sessionID].state.totalRequestsData+=currentData.size();
		}
		else //reply
		{
			httpdata.type=1;
			(*_httpSessionsTable)[sessionID].state.totalRepliesData+=currentData.size();
		}
		httpdata.data=currentData;
		//push the httpdata struct to the sessions httpData array(vector)
		(*_httpSessionsTable)[sessionID].httpData.push_back(httpdata);

	}//while(1)

}
//===========================================================================
//
// setSessionId(): Parameters: (1) sessID - a reference to the string that holds the sessions ID
//							   (2) tcpParams - a reference to the TCP_PARAMS struct that holds the tcp connection parameters
//
void SessionTracker::setSessionId(struct TCP_PARAMS &tcpParams,string& sessID)
{
	string sessid1;
	string sessid2;
	stringstream oss1;
	stringstream oss2;
	stringstream oss3;
	stringstream oss4;

	sessid1.assign(tcpParams.ip_src);
	sessid1.append(":");
	oss1<<tcpParams.src_port;
	sessid1.append(oss1.str());
	sessid1.append("-");
	sessid1.append(tcpParams.ip_dst);
	sessid1.append(":");
	oss2<<tcpParams.dst_port;
	sessid1.append(oss2.str());

	sessid2.assign(tcpParams.ip_dst);
	sessid2.append(":");
	oss3<<tcpParams.dst_port;
	sessid2.append(oss3.str());
	sessid2.append("-");
	sessid2.append(tcpParams.ip_src);
	sessid2.append(":");
	oss4<<tcpParams.src_port;
	sessid2.append(oss4.str());

	sessID.assign( sessid1 >= sessid2 ? sessid1 : sessid2 );
}

//===========================================================================
//
// parseRawData()
//
int SessionTracker::parseRawData(struct RAWIPDATA &raw,struct TCP_PARAMS &tcpParams,struct SEQACK_PARAMS &seqackParams,u_char **httpData)
{
	int httpDataLen;
	struct myip *ip;
	struct mytcp *tcp;

	//Pointer arithmetics
	ip=(struct myip*)(raw.data);
	tcp=(struct mytcp*)(raw.data+IP_HL(ip)*4);
	*httpData=raw.data + (IP_HL(ip)*4) + (TCP_HL(tcp)*4);
	httpDataLen=raw.datalen - (IP_HL(ip)*4) - (TCP_HL(tcp)*4);

	if( MIN_HTTP_DATA_TO_HANDLE > httpDataLen )
		return 0;

	//setting the sniffed packet tcp/ip parameters
	tcpParams.ip_src=inet_ntoa(ip->ip_src);
	tcpParams.ip_dst=inet_ntoa(ip->ip_dst);
	tcpParams.src_port=ntohs(tcp->tcp_source_port);
	tcpParams.dst_port=ntohs(tcp->tcp_dest_port);

	if(tcpParams.src_port==_serverPort) //server - reply
	{
		seqackParams.server_seq=ntohl(tcp->tcp_seqno);
		seqackParams.server_ack=ntohl(tcp->tcp_ackno);
	}
	else //client - request
	{
		seqackParams.client_seq=ntohl(tcp->tcp_seqno);
		seqackParams.client_ack=ntohl(tcp->tcp_ackno);
	}

	return httpDataLen;
}

//===========================================================================
//
// printSessionsTable()
//
void SessionTracker::printSessionsTable()
{
	map<string,struct HTTP_SESSION>::iterator itr;

	for( itr = _httpSessionsTable->begin(); itr != _httpSessionsTable->end(); itr++ )
	{
		cout << "HTTP SESSION: " << itr->first<<endl;
		cout << "======================================================================================="<<endl;
		int size=itr->second.httpData.size();
		for(int i=0;i<size;i++)
		{
			cout <<itr->second.httpData[i].data<<"\n\n"<<endl;
		}
	}
}

//===========================================================================
//
// printSessionsToFile()
//
void SessionTracker::printSessionsToFile()
{

 fstream file("/opt/swaf/sessions.txt",ios::out);

 map<string,struct HTTP_SESSION>::iterator itr;

 vector<struct DATA>::iterator vec;

 	file << "_httpSessionsTable size --> "<<_httpSessionsTable->size()<<"\n\n"<<endl;
 	for( itr = _httpSessionsTable->begin(); itr != _httpSessionsTable->end(); itr++ )
 	{
 		file << "HTTP SESSION: " << itr->first<<endl;
 		file << "======================================================================================="<<endl;

 		for( vec = itr->second.httpData.begin(); vec != itr->second.httpData.end(); vec++ )
 		{
			;
			if(vec->type==0)
				file << "\n\n\n ----------------------> REQUEST:\n";
			else
				file << "\n\n\n ----------------------> REPLY:\n";

			file << vec->data << endl;
		}


 		//if(itr->second)
 	//	file <<itr->second.data<<"\n\n\n"<<endl;
	}
         file.close();

}


void SessionTracker::printtofile(string& str)
{
	static fstream file("/opt/swaf/http_sess.txt",ios::out);
	file << str;
	file.close();
}


//===========================================================================
//
// getServerPort()
//
void SessionTracker::getServerPort()
{
	const string sniffer_conf_file="/opt/swaf/conf/sniffer.conf";
	try
	{
		ConfigFile cf(sniffer_conf_file);
	   _serverPort=(cf.Value("web-server","port"));
	}
	catch(const char *e)
	{
	        cout <<"\nError --> Failed to read server port from "<<sniffer_conf_file<<endl;
			exit(1);
	}
}
